#include "BencodeParser.h"
#include "IntegerBencode.h"
#include "ListaBencode.h"
#include "DiccionarioBencode.h"
#include "ElementoBencode.h"
#include "CadenaBencode.h"

BencodeParser::BencodeParser() {

  this->offsetFinalTorrentInfo = 0;
  this->offsetInicialTorrentInfo = 0;
}

BencodeParser::~BencodeParser() {


}

ElementoBencode* BencodeParser::getElemento(stringstream &stream){

	  char buffer;

	  stream.read(&buffer,1);

	  switch(buffer){

	  case 'l':
		  return this->crearLista(stream);
	  case 'd':
		  return this->crearDiccionario(stream);
	  case 'i':
		  return this->crearInteger(stream);
	  case '1'...'9':
	      stream.seekg (-1,ios_base::cur);
	      return this->crearCadena(stream);
	  case 'e':
		  return NULL;
	  default :
		  cerr<<"Error: Stream invalido."<<endl;
		  return NULL;
	  }

	return NULL;
}

ElementoBencode*  BencodeParser::crearLista(stringstream &stream) {

	ElementoBencode* nuevaLista = new ListaBencode();
	list<ElementoBencode*>* lista = new list<ElementoBencode*>();
	ElementoBencode* elemento;

	while ((elemento = this->getElemento(stream))){
		lista->push_back(elemento);
	}

	((ListaBencode*)nuevaLista)->setLista(lista);

	return nuevaLista;
}

ElementoBencode* BencodeParser::crearInteger(stringstream &stream) {

	string strEntero;
	unsigned long long int entero;
	ElementoBencode* nuevoInteger = new IntegerBencode();

	getline(stream,strEntero,'e');
	stringstream ss(strEntero);

	ss>>entero;

	if ( ss.fail()){
		cerr << "Error: Se ha intentado asignar un numero demasiado grande." << endl;
		delete nuevoInteger;
		return NULL;
	}

	((IntegerBencode*)nuevoInteger)->setValor(entero);

	return nuevoInteger;
}

ElementoBencode* BencodeParser::crearCadena(stringstream &stream) {

	ElementoBencode* nuevaCadena = new CadenaBencode();
	string strLongitud;
	string cadena;

	char* aux;
	int longitud;

	getline(stream,strLongitud,':');
	stringstream ss(strLongitud);

	ss>>longitud;

	if ( longitud <= 0){
		cerr << "Error: cadena invalida" << endl;
		delete nuevaCadena;
		return NULL;
	}

	aux = new char[longitud + 1];

	stream.read(aux,longitud);

    cadena.append( aux , longitud );

	((CadenaBencode*)nuevaCadena)->setValor(cadena);

	delete [] aux;

	return nuevaCadena;
}

ElementoBencode* BencodeParser::crearDiccionario(stringstream &stream) {


	ElementoBencode* clave;
	string strClave;
	ElementoBencode* elemento;
	bool torrentInfo = false;

	ElementoBencode* nuevoDiccionario = new DiccionarioBencode();
	map<string,ElementoBencode*>* mapa = new map<string,ElementoBencode*>();

	((DiccionarioBencode*)nuevoDiccionario)->setDiccionario(mapa);

	while ( (clave = getElemento(stream))){

		strClave = ((CadenaBencode*)clave)->getValor();

		if ( strClave == "info") {
			torrentInfo = true;
			this->offsetInicialTorrentInfo = stream.tellg();

		}

		elemento = getElemento(stream);

		if ( torrentInfo ) {
			torrentInfo = false;
			this->offsetFinalTorrentInfo = stream.tellg();

		}

		(*mapa)[strClave] = elemento;
		delete clave;

		if ( elemento == NULL){
			this->borrarRecursosInstanciados(nuevoDiccionario);
			return NULL;
		}
	}

	return nuevoDiccionario;
}




void BencodeParser::borrarRecursosInstanciados(ElementoBencode* elemento){

	if ( elemento != NULL) {

		if ( (elemento->getTipo() == 's') || (elemento->getTipo() == 'i')){
			delete elemento;
		} else if (elemento->getTipo() == 'd') {

			map<string,ElementoBencode*>* diccionario = ((DiccionarioBencode*)elemento)->getDiccionario();
			map<string,ElementoBencode*>::iterator it = diccionario->begin();

			for ( ; it != diccionario->end() ; it++){
				this->borrarRecursosInstanciados((*it).second);
			}

			delete elemento;
		} else if ( elemento->getTipo() == 'l'){

			list<ElementoBencode*>* lista = ((ListaBencode*)elemento)->getLista();
			list<ElementoBencode*>::iterator it = lista->begin();

			for ( ; it != lista->end() ; it++){
				this->borrarRecursosInstanciados((*it));
			}

			delete elemento;
		}
	}
}


unsigned long long int BencodeParser::getOffsetFinalTorrentInfo() const {

	return this->offsetFinalTorrentInfo;
}

unsigned long long int BencodeParser::getOffsetInicialTorrentInfo() const {

	return this->offsetInicialTorrentInfo;
}
